const { Sequelize } = require('sequelize');
const mongoose = require('mongoose')
const Op = Sequelize.Op
const operatorsAliases = {
    $eq: Op.eq,
    $ne: Op.ne,
    $gte: Op.gte,
    $gt: Op.gt,
    $lte: Op.lte,
    $lt: Op.lt,
    $not: Op.not,
    $in: Op.in,
    $notIn: Op.notIn,
    $is: Op.is,
    $like: Op.like,
    $notLike: Op.notLike,
    $iLike: Op.iLike,
    $notILike: Op.notILike,
    $regexp: Op.regexp,
    $notRegexp: Op.notRegexp,
    $iRegexp: Op.iRegexp,
    $notIRegexp: Op.notIRegexp,
    $between: Op.between,
    $notBetween: Op.notBetween,
    $overlap: Op.overlap,
    $contains: Op.contains,
    $contained: Op.contained,
    $adjacent: Op.adjacent,
    $strictLeft: Op.strictLeft,
    $strictRight: Op.strictRight,
    $noExtendRight: Op.noExtendRight,
    $noExtendLeft: Op.noExtendLeft,
    $and: Op.and,
    $or: Op.or,
    $any: Op.any,
    $all: Op.all,
    $values: Op.values,
    $col: Op.col
}
const path = require('path')
const fs = require('fs')

let db = {}
let default_db_name = ''

let M = (model, name) => {
    name = name || default_db_name
    return db[name][model]
}

exports.init = async (config) => {
    let _db_names = Object.keys(config.db)

    if (!_db_names.length) return true;

    // default_db_name 默认取第一个数据库配置
    config.default_db_name = config.default_db_name || _db_names[0]
    let default_model_path = config['default_model_path'] || 'resource/model'

    // 检查default_db_name 参数
    if (!_db_names.includes(config.default_db_name)) throw new Error(`default_db_name:${config.default_db_name} is not defined`)
    default_db_name = config.default_db_name

    let db_config = config.db

    for (let i = 0; i < _db_names.length; i++) {
        let name = _db_names[i]
        let cname = db_config[name]
        cname.operatorsAliases = operatorsAliases

        let model_dir = path.join(process.cwd(), default_model_path, name)
        fs.existsSync(model_dir) || fs.mkdirSync(model_dir)

        if (db_config[name]['dialect'] === 'mongo') {
            let models = {}
            let conn = await mongoose.createConnection(db_config[name].url, db_config[name].options).asPromise()
            let files = fs.readdirSync(model_dir).filter((file) => {return (path.extname(file) === '.js')})
            for (let i = 0; i < files.length; i++) {
                let model = require(path.join(process.cwd(), default_model_path, name, files[i]))(conn)
                models[model.modelName] = model
            }

            db[name] = models
            db[name].dialect = db_config[name]['dialect']
            db[name].conn = conn
            db[name].Mongoose = mongoose
            db[name].conn.transaction = (fn) => new Promise(async (resolve, reject) => {
                try {
                    let _session = null
                    try {
                        _session = await conn.startSession({
                            readConcern: { level: 'snapshot' },
                            writeConcern: { w: 'majority' }
                        })
                        await _session.startTransaction()
                        console.debug('startTransaction: ')
                        await fn(_session)
                        _session && await _session.commitTransaction()
                        console.debug('SUCCESS commitTransaction: ')
                        resolve(true)
                    } catch (e) {
                        _session && await _session.abortTransaction()
                        console.debug('ERROR abortTransaction: ', e)
                        reject(e)
                    } finally {
                        _session && await _session.endSession()
                        console.debug('finally endSession: ')
                    }
                } catch (e) {
                    reject(e)
                }
            })
        } else {
            let models = {}
            let conn = new Sequelize(db_config[name].database, db_config[name].username, db_config[name].password, cname)
            let files = fs.readdirSync(model_dir).filter((file) => {return (path.extname(file) === '.js') && file !== 'init-models.js'})
            for (let i = 0; i < files.length; i++) {
                let model = require(path.join(process.cwd(), default_model_path, name, files[i]))(conn, Sequelize.DataTypes)
                models[model.name] = model
            }

            db[name] = models
            db[name].dialect = db_config[name]['dialect']
            db[name].conn = conn
            db[name].Sequelize = Sequelize
        }
    }
}

exports.M = M
exports.DB = (name) => {
    name = name || default_db_name
    return db[name]
}
